package com.cqupt.scanning.security.filter;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.cqupt.scanning.security.entity.SysUser;
import com.cqupt.scanning.security.utils.JwtTokenUtil;
import com.cqupt.scanning.security.utils.ResponseUtil;
import com.cqupt.scanning.system.acl.entity.AclUser;
import com.cqupt.scanning.system.acl.service.AclUserService;
import com.cqupt.scanning.system.utils.result.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;

/**
 * @Data: 2021/7/12 10:00
 * @Author: 宋宝梁
 */
@Slf4j
public class LoginFilter extends UsernamePasswordAuthenticationFilter {

    private AuthenticationManager authenticationManager;
    private JwtTokenUtil jwtTokenUtil;
    private RedisTemplate<String, Object> redisTemplate;
    private AclUserService aclUserService;

    public LoginFilter(AuthenticationManager authenticationManager,
                       JwtTokenUtil jwtTokenUtil,
                       RedisTemplate<String, Object> redisTemplate,
                       AclUserService aclUserService) {
        this.authenticationManager = authenticationManager;
        this.jwtTokenUtil = jwtTokenUtil;
        this.redisTemplate = redisTemplate;
        this.aclUserService = aclUserService;
        this.setPostOnly(false);
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/scanning/security/login","POST"));
    }

    /**
     * 认证
     * @param req
     * @param res
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException {
        // 根据系统角色标识确定是什么用户登录
        String loginUsername = req.getParameter("username");
        String loginPassword = req.getParameter("password");
        String note = req.getParameter("note");
        try {
            if (StrUtil.hasEmpty(loginUsername)) {
                ResponseUtil.out(res, R.error().message("用户名不能为空"));
                return null;
            }
            if (StrUtil.hasEmpty(note)) {
                ResponseUtil.out(res, R.error().message("未设置系统角色标识"));
                return null;
            }
            AclUser aclUser = aclUserService.selectByUsername(loginUsername);
            if (ObjectUtil.isNull(aclUser)) {
                ResponseUtil.out(res, R.error().message("用户名不存在"));
                return null;
            } else {
                String databaseNote = aclUser.getNote();
                if (!note.equals(databaseNote)) {
                    ResponseUtil.out(res, R.error().message("系统角色不一致"));
                    return null;
                }
            }
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUsername, loginPassword, new ArrayList<>());
//            setDetails(req, authenticationToken); // 保存客户端的ip和session
            return authenticationManager.authenticate(authenticationToken);
        } catch (BadCredentialsException e) {
            ResponseUtil.out(res, R.error().message("Password is wrong"));
            return null;
        } catch (Exception e) {
            ResponseUtil.out(res, R.error().message("Authentication exception"));
            e.printStackTrace();
            throw new RuntimeException("认证异常");
        }
    }

    /**
     * 登录成功
     * @param req
     * @param res
     * @param chain
     * @param auth
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth) {
        SysUser user = (SysUser) auth.getPrincipal();
        String token = jwtTokenUtil.generateToken(user.getUsername());
        redisTemplate.opsForValue().set(user.getUsername(), user.getPermissionValueList());
        ResponseUtil.out(res, R.ok().data("token", token));
    }

    /**
     * 登录失败
     * @param request
     * @param response
     * @param e
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
        ResponseUtil.out(response, R.error().message("Login failed"));
    }
}
